//ICS 314 - Sundial Project
//Yosetime Group
//Spring 2013

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;

/** The Sundial class paints a 2D sundial (bird's eye view) with the line of the shadow displayed
 * 	determined by setting this.angle 
 *  
 *  @author Kyle Berney
 */
public class Sundial extends JPanel implements Printable{
	private double angle = 0; 	//degrees, [0, 360)
	private int x1 = 0;			//x coordinate used in angleBetween
	private int y1 = 138;		//y coordianate used in angleBetween
	private int x = 0;			//x coordiante of endpoint of drawn angle line
	private int y = 138;		//y coordinate of endpoint of drawn angle line
	
	/**
	 * Constructor
	 */
	public Sundial(){
		super();
	}

	/**
	 * Sets the this.angle to the given double angle and
	 * repaints the panel so that the given angle is displayed on the Sundial
	 * 
	 * @param	angle	the desired angle to be displayed on the Sundial
	 */
	public void setAngle(double angle){
		if (angle >= 0){
			this.angle = angle;
		}
		else {
			this.angle = angle + 360;
		}
		
		this.repaint();
	}

	/**
	 * Returns this.angle
	 * 
	 * @return		value of this.angle
	 */
	public double getAngle(){
		return this.angle;
	}

	/**
	 * Returns the clockwise angle, in radians, between the line segements given by
	 * the (x, y) coordiante given through the arguments and
	 * the (x, y) coordiante given from this.x1 and this.y1
	 * Both line segments have start coordiates (0, 0)
	 * 
	 * @param	x	x coordinate of line segement
	 * @param	y	y coordiante of line segment
	 * @return		clockwise angle between the two line segments. [0, 2pi)
	 */
	public double angleBetween(int x, int y){
		double innerp = (this.x1 * x) + (this.y1 * y);
		double mag1 = Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2));
		double mag2 = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));

		if (x >= 0){
			return Math.acos(innerp / (mag1 * mag2));
		}
		else {
			return 2*Math.PI - Math.acos(innerp / (mag1 * mag2));
		}
	}
	
	/**
	 * Paints the panel with 2D graphics of the Sundial
	 * 
	 */
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		this.setBackground(Color.WHITE);

		Graphics2D g2 = (Graphics2D)g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

		int[] xPoints = {50, 326, 326, 50};
		int[] yPoints = {50, 50, 326, 326};
		g2.setColor(Color.BLACK);
		g2.drawPolygon(xPoints, yPoints, 4);
		double error = getPos();
		g2.drawLine(188, 188, this.x, this.y);

		String display = "Angle = " + angle + " degrees ";
		g2.drawString(display, 25, 20);
	}

	/**
	 * Finds the pixel position of the end point of the line
	 * to be drawn, such that the angle between the line given by
	 * the Cartesian coordiantes (this.x1, this.y1) and pixel position
	 * translated to Cartesian coordiantes (x, y) is close to this.angle
	 * The pixel positions (x, y) are stored in this.x and this.y, respectively
	 * 
	 * @return		the error in degrees of the desired angle, this.angle, and the actual displayed angle
	 * 				or -1 if failed
	 */
	protected double getPos(){
		double angle = this.angle % 360;
		double min = Double.MAX_VALUE;		

		if (angle == 0.){
			this.x = 188;
			this.y = 50;
			return 0.;
		}
		else if (angle > 0. && angle < 45.){
			this.y = 50;			
			for (int tempx = 189; tempx < 326; tempx++){
				double deg = angleBetween(tempx - 188, 138) * (180. / Math.PI);			
				double e = Math.abs(angle - deg);
				if (e < min){
					min = e;
					this.x = tempx;
				}
			}
			return min;			
		}
		else if (angle == 45.){
			this.x = 326;
			this.y = 50;
			return 0.;
		}
		else if (angle > 45. && angle < 90.){
			this.x = 326;
			for (int tempy = 51; tempy < 188; tempy++){
				double deg = angleBetween(138, 188 - tempy) * (180. / Math.PI);
				double e = Math.abs(angle - deg);
				if (e < min){
					min = e;
					this.y = tempy;
				}
			}
			return min;
		}
		else if (angle == 90.){
			this.x = 326;
			this.y = 188;
			return 0.;
		}
		else if (angle > 90. && angle < 135.){
			this.x = 326;
			for (int tempy = 189; tempy < 326; tempy++){
				double deg = angleBetween(138, 188 - tempy) * (180. / Math.PI);
				double e = Math.abs(angle - deg);
				if (e < min){
					min = e;
					this.y = tempy;
				}
			}
			return min;	
		}
		else if (angle == 135.){
			this.x = 326;
			this.y = 326;
			return 0.;
		}
		else if (angle > 135. && angle < 180.){
			this.y = 326;
			for (int tempx = 325; tempx > 188; tempx--){
				double deg = angleBetween(tempx - 188, -138) * (180. / Math.PI);
				double e = Math.abs(angle - deg);
				if (e < min){
					min = e;
					this.x = tempx;
				}
			}
			return min;
		}
		else if (angle == 180.){
			this.x = 188;
			this.y = 326;
			return 0.;
		}
		else if (angle > 180. && angle < 225.){
			this.y = 326;
			for (int tempx = 187; tempx > 50; tempx--){
				double deg = angleBetween(tempx - 188, -138) * (180. / Math.PI);
				double e = Math.abs(angle - deg);
				if (e < min){
					min = e;
					this.x = tempx;
				}
			}
			return min;
		}		
		else if (angle == 225.){
			this.x = 50;
			this.y = 326;
			return 0.;
		}
		else if (angle > 225. && angle < 270.){
			this.x = 50;
			for (int tempy = 325; tempy > 188; tempy--){
				double deg = angleBetween(-138, 188 - tempy) * (180. / Math.PI);
				double e = Math.abs(angle - deg);
				if (e < min){
					min = e;
					this.y = tempy;
				}
			}
			return min;
		}
		else if (angle == 270.){
			this.x = 50;
			this.y = 188;
			return 0.;
		}
		else if (angle > 270. && angle < 315.){
			this.x = 50;
			for (int tempy = 187; tempy > 50; tempy--){
				double deg = angleBetween(-138, 188 - tempy) * (180. / Math.PI);
				double e = Math.abs(angle - deg);
				if (e < min){
					min = e;
					this.y = tempy;
				}
			}
			return min;
		}
		else if (angle == 315.){
			this.x = 50;
			this.y = 50;
			return 0.;
		}
		else if (angle > 315. && angle < 360.){
			this.y = 50;			
			for (int tempx = 51; tempx < 188; tempx++){
				double deg = angleBetween(tempx - 188, 138) * (180. / Math.PI);			
				double e = Math.abs(angle - deg);
				if (e < min){
					min = e;
					this.x = tempx;
				}
			}
			return min;		
		}

		return -1.;
	}

	/**
	 * Prints the contents of the panel
	 */
	public int print(Graphics g, PageFormat p, int pageNum) throws PrinterException {
		if (pageNum > 0)
			return Printable.NO_SUCH_PAGE;

		Graphics2D g2 = (Graphics2D)g;
		g2.translate(p.getImageableX(), p.getImageableY());
		this.paint(g2);
		return Printable.PAGE_EXISTS;
	}
}
